package com.rickpan.controller;

import com.rickpan.dto.common.ApiResponse;
import com.rickpan.entity.FileInfo;
import com.rickpan.exception.BusinessException;
import com.rickpan.service.FileManagementService;
import com.rickpan.service.FileStorageService;
import com.rickpan.service.DownloadStatsService;
import com.rickpan.storage.StorageService;
import com.rickpan.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.servlet.http.HttpServletRequest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

/**
 * 文件下载控制器
 * 处理文件下载、预览等请求
 */
@RestController
@RequestMapping("/api/files")
public class FileDownloadController {
    
    private static final Logger logger = LoggerFactory.getLogger(FileDownloadController.class);
    
    @Autowired
    private FileManagementService fileManagementService;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private StorageService storageService;

    @Autowired
    private DownloadStatsService downloadStatsService;
    
    /**
     * 下载文件
     * GET /api/files/{fileId}/download
     */
    @GetMapping("/{fileId}/download")
    public ResponseEntity<?> downloadFile(
            @PathVariable Long fileId,
            HttpServletRequest request) {
        return downloadFileInternal(fileId, request, true);
    }

    /**
     * 流式下载文件（用于前端进度显示，不触发浏览器下载）
     * GET /api/files/{fileId}/stream
     */
    @GetMapping("/{fileId}/stream")
    public ResponseEntity<?> streamFile(
            @PathVariable Long fileId,
            HttpServletRequest request) {
        return downloadFileInternal(fileId, request, false);
    }

    /**
     * 内部下载方法
     */
    private ResponseEntity<?> downloadFileInternal(
            Long fileId,
            HttpServletRequest request,
            boolean forceDownload) {
        
        try {
            Long userId = SecurityUtils.getCurrentUserId();
            logger.info("用户 {} 下载文件: fileId={}", userId, fileId);

            // 获取文件信息
            FileInfo fileInfo = fileManagementService.getFileDetail(userId, fileId);
            logger.info("文件信息: id={}, name={}, path={}, size={}",
                       fileInfo.getId(), fileInfo.getOriginalName(), fileInfo.getFilePath(), fileInfo.getFileSize());
            
            if (fileInfo.getIsDirectory()) {
                throw new BusinessException("不能下载文件夹");
            }
            
            // 智能检测文件存储位置
            // 首先检查文件是否在 MinIO 中存在
            boolean existsInMinIO = false;
            if ("minio".equals(storageService.getStorageType())) {
                try {
                    existsInMinIO = storageService.fileExists(fileInfo.getFilePath());
                    logger.debug("MinIO文件存在检查: {} -> {}", fileInfo.getFilePath(), existsInMinIO);
                } catch (Exception e) {
                    logger.warn("检查MinIO文件存在性失败: {}", fileInfo.getFilePath(), e);
                }
            }

            // 检查文件是否在本地存储中存在
            Path localFilePath = fileStorageService.getAbsolutePath(fileInfo.getFilePath());
            boolean existsLocally = Files.exists(localFilePath);
            logger.debug("本地文件存在检查: {} -> {}", localFilePath, existsLocally);

            if (existsInMinIO) {
                // 文件在 MinIO 中：通过后端代理下载，确保强制下载
                try {
                    String downloadUrl = storageService.generateDownloadUrl(
                        fileInfo.getFilePath(),
                        java.time.Duration.ofMinutes(5), // 短期有效的URL用于代理下载
                        forceDownload,
                        fileInfo.getOriginalName()
                    );

                    logger.info("代理下载MinIO文件: fileId={}, url={}", fileId, downloadUrl);

                    // 直接重定向到MinIO URL（已设置正确的Content-Disposition）
                    return ResponseEntity.status(HttpStatus.FOUND)
                            .location(java.net.URI.create(downloadUrl))
                            .build();

                } catch (Exception e) {
                    logger.error("代理下载失败: fileId={}", fileId, e);
                    throw new BusinessException("下载失败: " + e.getMessage());
                }

            } else if (existsLocally) {
                // 文件在本地存储中：直接读取文件，支持Range请求
                logger.info("使用本地存储下载: fileId={}, path={}", fileId, localFilePath);

                // 记录下载开始
                downloadStatsService.recordDownloadStart(fileId, userId, fileInfo.getOriginalName(), fileInfo.getFileSize());

                return handleLocalFileDownload(fileInfo, localFilePath, request, forceDownload);

            } else {
                // 文件不存在
                logger.error("文件不存在: fileId={}, path={}, minioExists={}, localExists={}",
                           fileId, fileInfo.getFilePath(), existsInMinIO, existsLocally);
                throw new BusinessException("文件不存在");
            }

            
        } catch (Exception e) {
            logger.error("文件下载失败: fileId={}", fileId, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 处理本地文件下载，支持Range请求
     */
    private ResponseEntity<Resource> handleLocalFileDownload(FileInfo fileInfo, Path filePath, HttpServletRequest request, boolean forceDownload) throws IOException {
        long fileLength = fileInfo.getFileSize();
        String rangeHeader = request.getHeader("Range");

        // 构建响应头
        HttpHeaders headers = new HttpHeaders();
        headers.add("Accept-Ranges", "bytes");

        // 只有在强制下载时才设置attachment，流式下载时设置为inline避免触发浏览器下载
        if (forceDownload) {
            headers.add("Content-Disposition",
                "attachment; filename*=UTF-8''" + URLEncoder.encode(fileInfo.getOriginalName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20"));
        } else {
            headers.add("Content-Disposition",
                "inline; filename*=UTF-8''" + URLEncoder.encode(fileInfo.getOriginalName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20"));
        }

        // 设置MIME类型
        if (fileInfo.getMimeType() != null) {
            headers.setContentType(MediaType.parseMediaType(fileInfo.getMimeType()));
        } else {
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        }

        // 处理Range请求
        if (rangeHeader != null && rangeHeader.startsWith("bytes=")) {
            return handleRangeRequest(filePath, fileLength, rangeHeader, headers, fileInfo);
        } else {
            // 普通下载
            headers.setContentLength(fileLength);
            Resource resource = new InputStreamResource(new FileInputStream(filePath.toFile()));

            // 记录下载完成
            downloadStatsService.recordDownloadComplete(fileInfo.getId());

            return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
        }
    }

    /**
     * 从 MinIO URL 代理下载文件
     */
    private ResponseEntity<Resource> proxyDownloadFromMinIO(String minioUrl, FileInfo fileInfo, boolean forceDownload) throws Exception {
        logger.debug("代理下载MinIO文件: {}", minioUrl);

        // 使用 RestTemplate 或 OkHttp 从 MinIO URL 获取文件流
        java.net.URL url = new java.net.URL(minioUrl);
        java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(10000); // 10秒连接超时
        connection.setReadTimeout(30000);    // 30秒读取超时

        if (connection.getResponseCode() != 200) {
            throw new BusinessException("从MinIO获取文件失败: HTTP " + connection.getResponseCode());
        }

        // 创建临时文件来存储下载的内容
        java.io.InputStream inputStream = connection.getInputStream();
        java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();

        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }

        inputStream.close();
        connection.disconnect();

        byte[] fileBytes = outputStream.toByteArray();
        outputStream.close();

        // 创建 ByteArrayResource
        org.springframework.core.io.ByteArrayResource resource =
            new org.springframework.core.io.ByteArrayResource(fileBytes);

        // 对中文文件名进行URL编码
        String encodedFilename = java.net.URLEncoder.encode(fileInfo.getOriginalName(),
                                                           java.nio.charset.StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");

        // 根据forceDownload参数设置Content-Disposition
        String disposition = forceDownload ? "attachment" : "inline";

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                       disposition + "; filename*=UTF-8''" + encodedFilename)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
                .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileBytes.length))
                .body(resource);
    }

    /**
     * 构建下载响应
     */
    private ResponseEntity<Resource> buildDownloadResponse(Resource resource, FileInfo fileInfo, HttpServletRequest request) throws Exception {
        // 设置响应头
        HttpHeaders headers = new HttpHeaders();

        // 设置文件名（支持中文）
        String encodedFileName = URLEncoder.encode(fileInfo.getOriginalName(), StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");

        // 只使用UTF-8编码的文件名，避免中文字符编码问题
        headers.add(HttpHeaders.CONTENT_DISPOSITION,
                   "attachment; filename*=UTF-8''" + encodedFileName);

        // 设置内容类型
        String contentType = fileInfo.getMimeType();
        if (contentType == null || contentType.isEmpty()) {
            contentType = "application/octet-stream";
        }
        headers.add(HttpHeaders.CONTENT_TYPE, contentType);

        // 设置文件大小
        headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileInfo.getFileSize()));

        // 支持断点续传
        headers.add(HttpHeaders.ACCEPT_RANGES, "bytes");

        logger.info("文件下载成功: fileName={}, size={}",
                   fileInfo.getOriginalName(), fileInfo.getFileSize());

        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }

    /**
     * 预览文件
     * GET /api/files/{fileId}/preview
     */
    @GetMapping("/{fileId}/preview")
    public ResponseEntity<?> previewFile(@PathVariable Long fileId) {
        try {
            Long userId = SecurityUtils.getCurrentUserId();
            logger.info("用户 {} 预览文件: fileId={}", userId, fileId);
            
            // 获取文件信息
            FileInfo fileInfo = fileManagementService.getFileDetail(userId, fileId);
            
            if (fileInfo.getIsDirectory()) {
                throw new BusinessException("不能预览文件夹");
            }
            
            // 检查是否支持预览
            if (!isSupportedPreviewType(fileInfo.getMimeType())) {
                throw new BusinessException("不支持预览此文件类型");
            }
            
            // 根据存储类型处理文件预览
            if ("local".equals(storageService.getStorageType())) {
                // 本地存储：直接读取文件
                Path filePath = fileStorageService.getAbsolutePath(fileInfo.getFilePath());

                if (!Files.exists(filePath)) {
                    throw new BusinessException("文件不存在");
                }

                // 创建文件资源
                Resource resource = new InputStreamResource(new FileInputStream(filePath.toFile()));
                return buildPreviewResponse(resource, fileInfo);

            } else {
                // MinIO 存储：返回预签名 URL 的 JSON 响应
                String previewUrl = storageService.generatePreviewUrl(
                    fileInfo.getFilePath(),
                    java.time.Duration.ofMinutes(30)
                );

                logger.info("生成MinIO预览URL: fileId={}, url={}", fileId, previewUrl);

                // 返回 JSON 格式的 URL，让前端处理跳转
                Map<String, Object> response = new HashMap<>();
                response.put("previewUrl", previewUrl);
                response.put("fileName", fileInfo.getOriginalName());
                response.put("fileSize", fileInfo.getFileSize());
                response.put("mimeType", fileInfo.getMimeType());
                response.put("redirectType", "minio");

                return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(response);
            }
            
        } catch (Exception e) {
            logger.error("文件预览失败: fileId={}", fileId, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * 获取文件缩略图
     * GET /api/files/{fileId}/thumbnail
     */
    @GetMapping("/{fileId}/thumbnail")
    public ResponseEntity<Resource> getFileThumbnail(@PathVariable Long fileId) {
        try {
            Long userId = SecurityUtils.getCurrentUserId();
            
            // 获取文件信息
            FileInfo fileInfo = fileManagementService.getFileDetail(userId, fileId);
            
            // 检查是否有缩略图
            if (fileInfo.getThumbnailPath() == null || fileInfo.getThumbnailPath().isEmpty()) {
                // 如果没有缩略图，返回默认图标
                return getDefaultFileIcon(fileInfo.getMimeType());
            }
            
            // 获取缩略图路径
            Path thumbnailPath = fileStorageService.getAbsolutePath(fileInfo.getThumbnailPath());
            
            if (!Files.exists(thumbnailPath)) {
                return getDefaultFileIcon(fileInfo.getMimeType());
            }
            
            // 创建缩略图资源
            Resource resource = new InputStreamResource(new FileInputStream(thumbnailPath.toFile()));
            
            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_TYPE, "image/jpeg");
            headers.add(HttpHeaders.CACHE_CONTROL, "max-age=86400"); // 缓存24小时
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(resource);
            
        } catch (Exception e) {
            logger.error("获取缩略图失败: fileId={}", fileId, e);
            return getDefaultFileIcon("application/octet-stream");
        }
    }

    /**
     * 构建预览响应
     */
    private ResponseEntity<Resource> buildPreviewResponse(Resource resource, FileInfo fileInfo) {
        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, fileInfo.getMimeType());
        headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileInfo.getFileSize()));
        headers.add(HttpHeaders.CACHE_CONTROL, "max-age=3600"); // 缓存1小时

        logger.info("文件预览成功: fileName={}", fileInfo.getOriginalName());

        return ResponseEntity.ok()
                .headers(headers)
                .body(resource);
    }

    // 私有辅助方法
    
    /**
     * 处理Range请求（断点续传）
     */
    private ResponseEntity<Resource> handleRangeRequest(Path filePath, long fileSize, String rangeHeader,
                                                       HttpHeaders headers, FileInfo fileInfo) throws IOException {

        // 解析Range头：bytes=start-end
        String range = rangeHeader.substring(6); // 去掉"bytes="
        String[] ranges = range.split("-");

        long start = 0;
        long end = fileSize - 1;

        if (ranges.length >= 1 && !ranges[0].isEmpty()) {
            start = Long.parseLong(ranges[0]);
        }
        if (ranges.length >= 2 && !ranges[1].isEmpty()) {
            end = Long.parseLong(ranges[1]);
        }

        // 验证范围
        if (start > end || start >= fileSize) {
            headers.add("Content-Range", "bytes */" + fileSize);
            return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                .headers(headers)
                .build();
        }

        // 调整end值
        if (end >= fileSize) {
            end = fileSize - 1;
        }

        long contentLength = end - start + 1;

        // 设置206响应头
        headers.add("Content-Range",
            String.format("bytes %d-%d/%d", start, end, fileSize));
        headers.setContentLength(contentLength);

        // 创建范围输入流资源
        Resource resource = new RangeInputStreamResource(filePath.toFile(), start, contentLength);

        // 更新下载进度
        downloadStatsService.updateDownloadProgress(fileInfo.getId(), start + contentLength);

        logger.info("Range请求处理: fileId={}, range={}-{}/{}, contentLength={}",
                   fileInfo.getId(), start, end, fileSize, contentLength);

        return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
            .headers(headers)
            .body(resource);
    }

    /**
     * 范围输入流资源 - 支持指定起始位置和长度的文件读取
     */
    private static class RangeInputStreamResource extends InputStreamResource {
        private final java.io.File file;
        private final long start;
        private final long length;

        public RangeInputStreamResource(java.io.File file, long start, long length) throws IOException {
            super(createRangeInputStream(file, start, length));
            this.file = file;
            this.start = start;
            this.length = length;
        }

        private static FileInputStream createRangeInputStream(java.io.File file, long start, long length) throws IOException {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            randomAccessFile.seek(start);

            return new FileInputStream(randomAccessFile.getFD()) {
                private long remaining = length;

                @Override
                public int read() throws IOException {
                    if (remaining <= 0) {
                        return -1;
                    }
                    remaining--;
                    return super.read();
                }

                @Override
                public int read(byte[] b, int off, int len) throws IOException {
                    if (remaining <= 0) {
                        return -1;
                    }

                    int toRead = (int) Math.min(len, remaining);
                    int bytesRead = super.read(b, off, toRead);
                    if (bytesRead > 0) {
                        remaining -= bytesRead;
                    }
                    return bytesRead;
                }

                @Override
                public void close() throws IOException {
                    super.close();
                    randomAccessFile.close();
                }
            };
        }

        @Override
        public long contentLength() {
            return length;
        }

        @Override
        public String getFilename() {
            return file.getName();
        }
    }
    
    /**
     * 检查是否支持预览
     */
    private boolean isSupportedPreviewType(String mimeType) {
        if (mimeType == null) return false;
        
        return mimeType.startsWith("image/") ||
               mimeType.startsWith("text/") ||
               mimeType.equals("application/pdf") ||
               mimeType.equals("application/json") ||
               mimeType.equals("application/xml");
    }
    
    /**
     * 获取默认文件图标
     */
    private ResponseEntity<Resource> getDefaultFileIcon(String mimeType) {
        // 这里可以根据文件类型返回不同的默认图标
        // 暂时返回404，实际项目中应该返回默认图标
        return ResponseEntity.notFound().build();
    }
}
